package com.mybatis.util;

import basic.framework.components.mybatis.annotation.Ignore;
import basic.framework.components.mybatis.annotation.Lazy;
import basic.framework.components.mybatis.common.utils.HumpUtils;
import basic.framework.components.mybatis.executor.dto.PrimaryKeyDto;
import com.mybatis.dto.MyColumnDto;
import com.mybatis.dto.MyEntityDto;
import com.mybatis.dto.MyModelDto;
import com.mybatis.dto.MyPrimaryKeyDto;
import org.springframework.beans.BeanUtils;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class MyModelUtil {
    private static final Map<Integer,MyEntityDto> coMap = new ConcurrentHashMap<>();
    public static MyEntityDto generateModelDto(Class<?> modelClass)
    {
        String className = modelClass.getCanonicalName();
        //可能考虑到是内部类，但是也不该有两个美元符号啊
        if (className.contains("$$")) {
            className = className.substring(0, className.indexOf("$$"));
            try {
                modelClass = Class.forName(className);
            } catch (ClassNotFoundException var13) {
                var13.printStackTrace();
            }
        }
        MyEntityDto myEntityDto = coMap.get(modelClass.hashCode());
        if (null==myEntityDto)
        {
            myEntityDto = new MyEntityDto();
            myEntityDto.setTableName(extractTableName(modelClass));
            List<MyModelDto> modelDtoList = getAllFieldsExcludeTransient(modelClass);
            //接下来便利 modelDtoList,将其转化为cloumndto，塞入entitydto，不用iterator，因为没有对集合本身进行操作
            for (MyModelDto myModelDto : modelDtoList)
            {
                //因为modeldto拥有field属性，所以直接可以getannotation
                Id id = myModelDto.getAnnotation(Id.class);
                GeneratedValue generatedValue = myModelDto.getAnnotation(GeneratedValue.class);
                Column column = myModelDto.getAnnotation(Column.class);
                Lazy lazy = myModelDto.getAnnotation(Lazy.class);
                Ignore ignore = myModelDto.getAnnotation(Ignore.class);
                MyColumnDto myColumnDto = new MyColumnDto();
                if (lazy != null) {
                    myColumnDto.setLazy(true);
                }
                if (ignore != null) {
                    myColumnDto.setIgnore(true);
                }

                String columnName;
                if (null!= column&&column.name().trim().length()>0)
                {
                    myColumnDto.setColumn(column);
                    columnName = column.name();
                }
                else
                {
                    //如果没有@column注解，则使用属性名的数据库法则
                    columnName = HumpUtils.humpToLine(myModelDto.getField().getName());
                }
                myColumnDto.setColumnName(columnName);
                if (null!=id)
                {
                    loadPrimaryKey(myModelDto, id, columnName, generatedValue, myEntityDto);
                }
                myColumnDto.setPk(id!=null);
                myColumnDto.setJavaType(myModelDto.getField().getType());
                myColumnDto.setMyModelDto(myModelDto);
                myEntityDto.getMap().put(myModelDto.getField().getName(),myColumnDto);
            }
            coMap.put(modelClass.hashCode(),myEntityDto) ;
        }
        return myEntityDto;
    }

    private static void loadPrimaryKey(MyModelDto myModelDto, Id id, String columnName,
                                       GeneratedValue generatedValue, MyEntityDto myEntityDto) {
        MyPrimaryKeyDto primaryKeyDto = new MyPrimaryKeyDto();
        primaryKeyDto.setPropertyName(myModelDto.getField().getName());
        primaryKeyDto.setCloumnName(columnName);
        if (null!=generatedValue)
        {
            //这边auto属性是交给mysql去管理，也可以直接就设置成自增长，就不设置了
            primaryKeyDto.setGenerationType(generatedValue.strategy());
        }
        myEntityDto.setMyPrimaryKeyDto(primaryKeyDto);
    }

    private static List<MyModelDto> getAllFieldsExcludeTransient(Class<?> modelClass) {
        List<Field> fields = MyReflectionUtils.getAllFieldsExcludeTransient(modelClass);
        //可以用for each 循环，但是迭代器效率更高
        //JDK内省类库：PropertyDescriptor类：,锁总通过得到数组，然后循环field，可以试试另一种
        Iterator iterator = fields.iterator();
        List<MyModelDto> modelDtoList = new ArrayList<>();
        while (iterator.hasNext())
        {
            Field field = (Field) iterator.next();
            //属性描述器，jdk缺省api
            PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(modelClass, field.getName());
            Method read = pd.getReadMethod();
            Method write = pd.getWriteMethod();
            MyModelDto myModelDto = new MyModelDto();
            if (null!=read)
            {
                myModelDto.setField(field);
                myModelDto.setGetMethod(read);
                myModelDto.setSetMethod(write);
                modelDtoList.add(myModelDto);
            }
        }
        return modelDtoList;
    }

    private static String extractTableName(Class<?> modelClass) {
        Table table = modelClass.getAnnotation(Table.class);
        if (null!=table&&table.name().trim().length()>0)
        {
            return table.name();
        }
        else {
            //这边考虑如果没有默认为接口的类名并转化成小写拼接_，以及有内部类的情况
            String tableName = HumpUtils.humpToLine(modelClass.getSimpleName());//正则表达式，麻烦
            if (modelClass.getSimpleName().contains("$")) {
                tableName = tableName.substring(0, tableName.indexOf("$"));
            }

            while(tableName.startsWith("_")) {
                tableName = tableName.substring(1);
            }

            return tableName;
        }
    }
}
